package org.quipu.domain;

import java.io.Serializable;
import java.util.Collection;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "CATEGORY", uniqueConstraints = @UniqueConstraint(columnNames = {"ID_BOOK", "TITLE", "ID_PARENT", "TRANSACTION_TYPE"}))
@NamedQueries({
    @NamedQuery(name = "Category.findAll", query = "SELECT c FROM Category c"),
    @NamedQuery(name = "Category.findById", query = "SELECT c FROM Category c WHERE c.id=:id"),
    @NamedQuery(name = "Category.findByTitleAndParentAndTypeForBook", query = "SELECT c FROM Category c WHERE c.title=:title AND c.parentCategory.id=:parentId AND c.type=:type AND c.book.id=:bookId"),
    @NamedQuery(name = "Category.findRootByTitleAndTypeForBook", query = "SELECT c FROM Category c WHERE c.title=:title AND c.parentCategory is null AND c.type=:type AND c.book.id=:bookId")
})
public class Category extends UniqueEntity implements Serializable {

    @JoinColumn(name = "ID_PARENT", referencedColumnName = "ID", nullable = true)
    @ManyToOne
    private Category parentCategory;

    @Column(name = "TITLE", length = 64, nullable = false)
    private String title;

    @JoinColumn(name = "ID_BOOK", referencedColumnName = "ID", nullable = false)
    @ManyToOne
    private Book book;

    // todo change column name from TYPE to something like TRANSACTION_TYPE; change java type from Integer to enum
    @Column(name = "TRANSACTION_TYPE")
    @Enumerated(EnumType.STRING)
    private Transaction.Type type;

    @OneToMany(mappedBy = "parentCategory", cascade = CascadeType.REMOVE)
    private Collection<Category> childs;

    @OneToMany(mappedBy = "category", cascade = CascadeType.REMOVE)
    private Collection<Transaction> transactions;

    public Category getParentCategory() {
        return parentCategory;
    }

    public void setParentCategory(Category parentCategory) {
        this.parentCategory = parentCategory;
    }

    public boolean isChild() {
        return (parentCategory != null);
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    private String getFullTitle(Category category) {
        String fullTitle = "";
        if(category != null) {
            fullTitle = getFullTitle(category.getParentCategory());
            if(!fullTitle.isEmpty())
                fullTitle += " > ";
            fullTitle += category.getTitle();
        }
        return fullTitle;
    }

    public int getDepth() {
        if(parentCategory == null) {
            return 0;
        } else {
            return 1 + parentCategory.getDepth();
        }
    }

    public String getFullTitle() {
        return getFullTitle(this);
    }

    public Book getBook() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }

    public Transaction.Type getType() {
        return type;
    }

    public void setType(Transaction.Type type) {
        this.type = type;
    }

    public Collection<Category> getChilds() {
        return childs;
    }

    public void setChilds(Collection<Category> childs) {
        this.childs = childs;
    }

    public Collection<Transaction> getTransactions() {
        return transactions;
    }

    public void setTransactions(Collection<Transaction> transactions) {
        this.transactions = transactions;
    }

    public boolean isGrandChildOf(Category category) {
        if(category == null || parentCategory == null)
            return false;
        else if(category.equals(parentCategory))
            return true;
        else
            return parentCategory.isGrandChildOf(category);
    }

    public Category getRootParent() {
        if(parentCategory == null) {
            return null;
        } else {
            Category rootParent = parentCategory.getRootParent();
            if(rootParent == null)
                rootParent = parentCategory;
            return rootParent;
        }
    }

    @Override
    public String toString() {
        return "{ id=" + getId() + "; title=" + getTitle() + "; }";
    }

    @Override
    public boolean equals(Object category) {
        return category instanceof Category && getId().equals(((Category)category).getId());
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 59 * hash + (getId() != null ? this.getId().hashCode() : 0);
        return hash;
    }
}
